//
// Created by QU on 2023/12/17.
//

// c++ standard library
#include <iostream>
#include <list>
#include <vector>
#include <map>
#include <set>


// boost library
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/smart_ptr/owner_less.hpp>

using std::cin, std::cout, std::endl;

using boost::shared_ptr;

class My_shared {
private:
    shared_ptr<int> p;
public:
    My_shared(shared_ptr<int> p_): p(p_){}
    ~My_shared(){}

    void print(){
        cout << "count: " << p.use_count() << " value= " << *p << endl;
    }
};

void some_operation(void * p);
void print_func(shared_ptr<int> ptr);

int main(){
    // shared_ptr 比较复杂。 最有价值的，最重要的组成部分。
    // shared_ptr维护一个引用计数器，每当创建了一个shared_ptr从另一个shared_ptr获得指针的管理权的时候，这个计数器+1.
    // 从而两个shared_ptr共享一个指针的管理权
//    shared_ptr(shared_ptr const & r) 和 operator=() 就是用来完成这个工作的
    // 同时也可以像unique_ptr一样指定删除器.

    // reset()函数和scoped_ptr/unique_ptr不一样,  scoped_ptr/unique_ptr将会把指针修改为0(nullptr),
    // shared_ptr将会将计数器-1(当0的时候删除);

    // aliasing: 高级用法, 别名构造函数.   可以不增加引用计数的一种方式.

    // unique(): 检查是不是唯一引用的指针(如果存在两个shared_ptr为false.  use_count()不推荐, 虽然可以判断use_count() == 1.

    // 支持比较运算,  仅提供了openator<() , 比较内部的指针, 相当于 a.get() == b.get(); 使得其可以使用在map,set等关联容器上.
    // 支持操作符operator<<(), 打印内部的指针值, 方便调试.

    shared_ptr<int> sp(new int(10));
    assert(sp.unique());

    // 通过shared_ptr的operator=(), 增加一个+1引用sp2
    shared_ptr<int> sp2 = sp;

    // clangd的提示说, 尽量不要使用拷贝, 让我替换成这个.   (也就是创建一个别名引用). 如果创建了引用, 不能+1, assert过不去.
    const shared_ptr<int>& sp3 = sp;
    assert(sp == sp2 && sp.use_count() == 2);

    *sp = 100;
    assert(*sp = 100); assert(*sp2 = 100);

    sp.reset();
    assert(!sp);

    /////////////////////////////////////////////////////////////////////////////////////////////////////////

    shared_ptr<int> p(new int (100));
    My_shared shared1(p), shared2(p);

    shared1.print();
    shared2.print();

    *p = 20;
    print_func(p);

    shared1.print();

    // 现在shared_ptr已经帮助你基本删除了delete关键字.
    // 现在需要删除new关键字, smart_ptr库提供了一个工厂函数: make_shared() 消除显示的new调用.
    // make_shared()可以接受多个参数, 把他传递给类型T的构造函数, 创建一个shared_ptr<T>对象并返回.
    // 使用make_shared更加高效.

//    template< class T, class... Args > typename boost::detail::sp_if_not_array< T >::type make_shared( Args && ... args )
    // 在不支持可变形参的c++11以前, 最多10个参数

    // 创建仅仅包含一个string对象的.
    auto makeShared_string = boost::make_shared<std::string>("make shared");
    cout << *makeShared_string << endl;

    auto makeMultiShared_string = boost::make_shared<std::vector<int>>(100,200);
    cout << makeMultiShared_string->at(1) << endl;

    // 除了make_shared还有allocate_shared, allocate_shared多一个定制内存分配器参数, 其他两者相同.

    /////////////////////////////////////////////////////////////////////////////////////

    // 与标准容器配合使用.
    // 1.    shared_ptr<std::list<int>>     实现共享访问指针, 比较简单
    // 2.    std::vector<shared_ptr<int>>   标准容器没有办法自动管理指针类型的元素, 必须编写额外的代码来保证元素的删除
    //          使用这种方式可以保证指针被正确的删除.

    typedef std::vector<shared_ptr<int>> vec_shared;
    vec_shared v(10);

    int i = 0;
    // 这里的clangd也提醒我换成下面的格式. 下面的是我自己写的, 和书上一样, 好耶!
    for (auto pos = v.begin(); pos != v.end(); ++pos) {
        (*pos) = boost::make_shared<int>(++i);
        cout << *(*pos) << ", ";
    }
    cout << endl;

    for (auto index: v) {
        index = boost::make_shared<int>(++i);
        cout << (*index) << ", ";
    }
    cout << endl;

    auto ptr = v[9];        // 这里v存着的是shared_ptr. 如果想要使用其中的内容, 需要进行解引用.
    *ptr = 120;
//    或者这样的  **v 得到 vector 对象.

    // printer
    for (const auto& index: v) {
        cout << (*index) << ", ";
    }
    cout << endl;

    // boost.iterators提供了迭代器适配器, 来简化容纳shared_ptr容器的使用.

    /////////////////////////////////////////////////////////////////////////////////////////////

    // 应用于桥接模式 (没学过这东西啊) pimpl handle/body
//    scoped_ptr和shared_ptr都可以实现桥接模式. shared_ptr更加适合使用, 因为shared_ptr有拷贝复制功能等.

    // 应用在工厂模式 (make_xxx??)

    // 定制删除器   shared_ptr提供删除器
    //    shared_ptr<class T, D d>  delete p 将会替换成 d(p);
    // 下面是一个例子

//    class socket_t {};
//
//    socket_t* open_socket(){
//        cout << "open Socket: " << endl;
//        return new socket_t;
//    }
//
//    socket_t* close_socket(socket_t* ptr_socket){
//        cout << "Socket close." << endl;
//        // ...
//    }
//
//    socket_t* socket = open_socket();
//    shared_ptr<socket_t>(socket, close_socket);

    /////////////////////////////////////////////////////////////////////////////////////////////

    // 对于传统的C语言程序. 比如说打开文件
//    shared_ptr<FILE> fp(fopen("./1.txt", "r"), fclose);

    // 对比原生的c++11的shared_ptr
    // std的shared_ptr是隐式bool类型转换, 后来更改成为显式的bool类型转换, 使用static_cast<bool>(p);
    // 出于兼容性考虑, 在if, assert, for等等逻辑判断语境下, 还是可以使用隐式转换的, 但是在别的环境中, 就不能使用这样的隐式转换了.

    /////////////////////////////////////////////////////////////////////////////////////////////

    // shared_ptr 指针类型转换函数
    // 在子类和父类的语境中, 类型转换一直是一个常用的东西.
    // 你不能使用类似这样的对指针类型进行强制类型转换, 因为这样shared_ptr管理这个指针的时候将会发生错误(无法正常管理).
//    static_cast<T *>(p.get())     // 虽然语法是正确的, 但是运行的时候将会出现意外.
// shared_ptr提供了这个问题的解决方案. 你可使用static_pointer_cast<T>(), const_pointer_cast<T>(), dynamic_pointer_cast<T>().
    //  下面的内容将会把bad_exception的shared_ptr指针转换成父类exception.
    shared_ptr<std::exception> p_exception(new std::bad_exception);
    std::shared_ptr<std::exception> std_p_exception(new std::bad_exception);

    // 使用标准的dynamic_cast是不正确的, 应该使用dynamic_pointer_cast.
//    auto p_exception1 = dynamic_cast<std::bad_exception> (p_exception);

    // 标准库的实现.
    auto p_exception1 = std::dynamic_pointer_cast<std::exception> (std_p_exception);

    // boost的实现.
    auto p_exception2 = boost::static_pointer_cast<std::exception>(p_exception);
    auto p_exception3 = boost::dynamic_pointer_cast<std::exception>(p_exception);

    assert(p_exception == p_exception3);

    /////////////////////////////////////////////////////////////////////////////////////////////
    //    shared_ptr<void> // 高级用法
// shared_ptr<void> 可以存储void*指针, void* 可以指向任何类型, 因此shared_ptr<void>就像一个泛型的指针容器, 拥有容纳任意类型指针的能力.

    /////////////////////////////////////////////////////////////////////////////////////////////
    // 基于定制删除器和 shared_ptr<void>, 可以实现使用shared_ptr删除器调用退出作用域的时候调用任意函数.
    // 就像下面的这样:

    // 创建一个shared_ptr指针, 指向的类型是void, 形成void* 类型, 让这个指针指向nullptr, 定制删除器为一个函数或者函数指针.
    shared_ptr<void> s_nullptr(nullptr, some_operation);
    // 似乎不能使用make_shared()创建这样指向void的带有定制删除器的指针.

    /////////////////////////////////////////////////////////////////////////////////////////////

    // 别名构造函数. (aliasing)
    // make_shared还有一种比较特殊的构造函数.
//    template <class Y>
//    shared_ptr( shared_ptr<Y> const & r, element_type * p);
    // 上面的函数的作用是共享r的引用计数, 但是这个shared_ptr持有的指针可能是一个和r毫不相关的指针p, 而且不负责p的自动销毁.

    // 这种构造比较奇怪, 但是实际上还是有利用的价值的
    // 一个具体的例子是使用一个指向已经被shared_ptr管理的对象的内部成员变量.
    auto ptr1 = boost::make_shared<std::pair<int,int>>(0,1);
    shared_ptr<int> ptr2(ptr1, &ptr1->second);

    assert(ptr1.use_count() == 2);
    assert(ptr1.use_count() == ptr2.use_count());

    assert(static_cast<void*>(ptr1.get()) != static_cast<void *>(ptr2.get()));
    assert(&ptr1->second == ptr2.get());

    /////////////////////////////////////////////////////////////////////////////////////////////

    // owner_less 因为上面的存在别名构造函数的原因, (某些情况下)单纯基于get()方法的指针值不再适用.
    // boost提供了owner_less 是一个提供基于所有权的比较对象, 定义了严格的弱序关系, 可以用在关联容器上.

    typedef shared_ptr<int> int_shared_ptr;     // 共享指针
    // 注意owner_less<>放的是 int_shared_ptr.
    typedef boost::owner_less<int_shared_ptr> int_shared_ptr_less;  // 函数对象

    int_shared_ptr s_p1(new int (10));  // 共享指针
    int n = 20;
    int_shared_ptr s_p2(s_p1, &n);    // 别名构造, 增加引用计数, 但是指向n

    assert(s_p1.get() != s_p2.get());       // 存储的指针不相等. 但是如果看引用, 他们相等 (下面的set)

    // 通过owner_less对象进行比较.
    assert(!int_shared_ptr_less()(s_p1,s_p2) && !int_shared_ptr_less ()(s_p2,s_p1));
    // 两者不大于也不小于, 表示为相等.

    typedef std::set<int_shared_ptr> int_set;
    int_set s;
    s.insert(s_p1);
    s.insert(s_p2);     // 这里set基于owner_less所有权比较, 会认为他们是一样的.

    assert(s.size() == 1);  // 得到的结果是1

    return 0;
}

void some_operation(void * p){
    cout << "some operation func invoked!" << endl;
}

void print_func(shared_ptr<int> ptr){
    cout << "count: " << ptr.use_count() << " value= " << *ptr << endl;
}


